//  This product includes software developed by Denim Group, Ltd. (http://www.denimgroup.com/)
//  All use and distribution of the Denim Group developed software is subject to Version 2.0
//  of the Apache License (http://www.apache.org/licenses/LICENSE-2.0).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using AppScan;
using AppScan.Scan.Results;
using AppScan.Scan.Data;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Diagnostics;
using System.Reflection;

namespace AppScan.DefectLogger
{
	public partial class LogDefectForm : Form
	{
		private const string descFieldName = "BG_DESCRIPTION";
		private const int inputWidth = 115;

        private string _tempDir = string.Empty;

        private List<string> _attachedFiles = new List<string>();

		DefectLoggerBase _extension = null;
		IIssue _issue;
		List<Control> _dataControls = new List<Control>();

        public string[] AttachedFiles
        {
            get
            {
                string[] retVal = _attachedFiles.ToArray();
                return (retVal);
            }
        }

        

		public LogDefectForm(DefectLoggerBase extension, IIssue issue, int top, int left)
		{
			_extension = extension;
			_issue = issue;

			InitializeComponent();

            if (extension.Config.UserName.Trim().Equals(string.Empty))
            {
                // force the user to run the configuration first
                UserNameTB.Enabled = false;
                PasswordTB.Enabled = false;
                DefectDetailsGroup.Enabled = false;
                DescriptionGroup.Enabled = false;
                groupBox1.Enabled = false;
                LogBtn.Enabled = false;
                setConfigLbl.Visible = true;
            }

			UserNameTB.Text = extension.Config.UserName;
			PasswordTB.Text = extension.Config.Password;
			
			// Fill the subject with a default value
			SubjectTB.Text =
				_issue.IssueType.Name + " in " + DefectLoggerBase.GetEntityDesc(_issue.Entity);

			// make a folder for the files created
			string tempDir;
			DirectoryInfo tempDirInfo;
			try
			{
				tempDir = Path.GetTempPath() + Path.DirectorySeparatorChar 
							+ Path.GetRandomFileName() + Path.DirectorySeparatorChar;
				tempDirInfo = Directory.CreateDirectory(tempDir);
				_tempDir = tempDir;
			}
			catch (Exception ex)
			{
				MessageBox.Show("Failed to create directory for temp files." + Environment.NewLine + ex.ToString());
				return;
			}

			// Write the Advisory & Fix Rec to temp HTML files
			string fileName = WriteAdvToFile(tempDir);
			AddFileToListView(fileName);
            _attachedFiles.Add(fileName);

			fileName = WriteFixRecToFile(tempDir);
			AddFileToListView(fileName);
            _attachedFiles.Add(fileName);

			// Write the request/response to a temp file
			int varInd = 1;
			foreach (IVariant variant in _issue.Variants)
			{
				string origFileName = tempDir + "Variant" + varInd.ToString() + "-Orig.txt";
				File.WriteAllText(origFileName, variant.OriginalRequestResponse);
				AddFileToListView(origFileName);
                _attachedFiles.Add(origFileName);

				string testFileName = tempDir + "Variant" + varInd.ToString() + "-Test.txt";
				File.WriteAllText(testFileName, variant.TestRequestResponse);
				AddFileToListView(testFileName);
                _attachedFiles.Add(testFileName);

				varInd++;
			}

			// Count the number of visible fields, to decide the number of rows
			int nFields = extension.Config.Fields.Count;

			// Add the fields
			bool showLeft = true;
			int rowInd = 0;
			for (int i = 0; i < nFields; i++)
			{
				Field field = extension.Config.Fields[i];

				// Skip fields that aren't visible
				if (!field.Visible)
					continue;

				// Create the label
				Label fieldLabel = new Label();
				fieldLabel.Text = field.DisplayName + ':';
				fieldLabel.AutoSize = true;
				if (field.Required)
					fieldLabel.ForeColor = Color.Red;

				// Create the control, based on the type
				Control dataControl = null;
				switch (field.FType)
				{
					case Field.FieldType.Text:
						dataControl = new TextBox();
						(dataControl as TextBox).Text = field.DefaultValue;
						break;
					case Field.FieldType.ReadOnly:
						dataControl = new TextBox();
						(dataControl as TextBox).Text = field.DefaultValue;
						(dataControl as TextBox).ReadOnly = true;
						break;
					case Field.FieldType.DropDown:
						dataControl = new ComboBox();

						// Populate the possible values
						(dataControl as ComboBox).Items.AddRange(field.PossibleValues.ToArray());
						(dataControl as ComboBox).SelectedIndex = 0;
						if (!string.IsNullOrEmpty(field.DefaultValue) && field.PossibleValues.Contains(field.DefaultValue))
						{
							(dataControl as ComboBox).SelectedItem = field.DefaultValue;
						}
						break;
					case Field.FieldType.Date:
						DateTimePicker datePicker = new DateTimePicker();
						datePicker.Format = DateTimePickerFormat.Short;
						dataControl = datePicker;
						
						try
						{
							if (!string.IsNullOrEmpty(field.DefaultValue))
								(dataControl as DateTimePicker).Value = DateTime.Parse(field.DefaultValue);
						}
						catch (Exception) { }
						break;
					case Field.FieldType.Checkbox:
						dataControl = new CheckBox();
						try
						{
							if (!string.IsNullOrEmpty(field.DefaultValue))
								(dataControl as CheckBox).Checked = Boolean.Parse(field.DefaultValue);
						}
						catch (Exception) { }
						break;

					default:
						MessageBox.Show("Invalid field type for field " + field.Name);
						continue;
				}

				dataControl.Anchor = AnchorStyles.Left;
				dataControl.Width = inputWidth;
				fieldLabel.Anchor = AnchorStyles.Right;


				// Store the original field on the data control, for use when logging the defect
				dataControl.Tag = field;

				// Add the field to the table
				FieldsTable.Controls.Add(fieldLabel, 0 + (showLeft ? 0 : 2) , rowInd);
				FieldsTable.Controls.Add(dataControl, 1 + (showLeft ? 0 : 2) , rowInd);

				// Add the new data control to the list
				_dataControls.Add(dataControl);

				// Modify the left/right status, and increase row ind if needed
				if (showLeft)
				{
					showLeft = false;
				}
				else
				{
					showLeft = true;
					rowInd++;
				}
			}
			DescriptionTB.Text = GenerateDescription();

			// position as the issue selector was positioned
			this.Top = top;
			this.Left = left;
		}



		private void CancelBtn_Click(object sender, EventArgs e)
		{
			Close();
		}

		private string GetValueFromDataControl(Control c)
		{
			// Get the configuration field
			Field field = c.Tag as Field;
			if (field == null)
				return null;

			switch (field.FType)
			{
				case Field.FieldType.Checkbox:
					return (c as CheckBox).Checked.ToString();
				case Field.FieldType.Date:
					return (c as DateTimePicker).Value.ToShortDateString();
				case Field.FieldType.DropDown:
					if ((c as ComboBox).SelectedItem == null)
					{
						(c as ComboBox).SelectedIndex = 0;
					}
					return (c as ComboBox).SelectedItem.ToString();
					
				case Field.FieldType.Text:
					return (c as TextBox).Text;
				case Field.FieldType.ReadOnly:
					return (c as TextBox).Text;
			}

			return null;
		}

		private string WriteAdvToFile(string dirName)
		{
			// XmlTextWriter writer = null;
            TextWriter writer = null;
			FileInfo fileInfo = null;
			string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar;
	
			try
			{
                /*  TODO - Make this use XSLT to make HTML rather than using raw XML
				#region XSL Arguments
				//Create an XsltArgumentList.
				XsltArgumentList xslAdvisoryArgs = new XsltArgumentList();
				xslAdvisoryArgs.AddParam("watermarkImagePath", string.Empty, "images");
				xslAdvisoryArgs.AddParam("css", string.Empty, File.ReadAllText(assemblyPath + "QCExport.css"));
				xslAdvisoryArgs.AddParam("threatClassificationTitle", string.Empty, "WASC Threat Classification");
				xslAdvisoryArgs.AddParam("securityRiskTitle", string.Empty, "Security Risk");
				xslAdvisoryArgs.AddParam("causesTitle", string.Empty, "Possible Causes");
				xslAdvisoryArgs.AddParam("technicalDescriptionTitle", string.Empty, "Technical Description");
				xslAdvisoryArgs.AddParam("affectedProductsTitle", string.Empty, "Affected Products");
				xslAdvisoryArgs.AddParam("cveTitle", string.Empty, "CVE Reference(s)");
				xslAdvisoryArgs.AddParam("referencesTitle", string.Empty, "References and Relevant Links");
				xslAdvisoryArgs.AddParam("na", string.Empty, "N/A");
				xslAdvisoryArgs.AddParam("copyright", string.Empty, "(C)");
				#endregion
                 */

                fileInfo = new FileInfo(dirName + "Advisory.html");

				// writer = new XmlTextWriter(fileInfo.FullName, null);
                writer = new StreamWriter(fileInfo.FullName);

				// Load the XSLT
                //  TODO - Make this create HTML rather than just using raw XML
				// XPathNavigator nav = new XPathDocument(assemblyPath + "advisory.xsl").CreateNavigator();
				// XslCompiledTransform xslt = new XslCompiledTransform();
				// xslt.Load(nav);

				// Create the HTML
                //  TODO - Switch this back to make HTML rather than just using raw XML
				//  xslt.Transform(_issue.IssueType.Advisory.CreateNavigator(), xslAdvisoryArgs, writer);
                writer.Write(_issue.IssueType.Advisory.InnerText);
			}
			finally
			{
				// Close writer
				if (writer != null)
				{
					writer.Flush();
					writer.Close();
				}
			}

			return fileInfo.FullName;
		}
		private string WriteFixRecToFile(string dirName)
		{
			// XmlTextWriter writer = null;
            TextWriter writer = null;

			FileInfo fileInfo = null;
			string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar;

			try
			{
				#region XSL Arguments
				//Create an XsltArgumentList.
                /* TODO - Make HTML again
				XsltArgumentList xslFixRecommendationArgs = new XsltArgumentList();
				xslFixRecommendationArgs.AddParam("showAsp.Net", string.Empty, true);
				xslFixRecommendationArgs.AddParam("showJ2ee", string.Empty, true);
				xslFixRecommendationArgs.AddParam("watermarkImagePath", string.Empty, "images");
				xslFixRecommendationArgs.AddParam("css", string.Empty, File.ReadAllText(assemblyPath + "QCExport.css"));
				xslFixRecommendationArgs.AddParam("fixRecommendationTitle", string.Empty, "Fix Recommendation");
				xslFixRecommendationArgs.AddParam("generalTitle", string.Empty, "General");
				xslFixRecommendationArgs.AddParam("aspTitle", string.Empty, "Asp.Net");
				xslFixRecommendationArgs.AddParam("j2eeTitle", string.Empty, "J2EE");
				xslFixRecommendationArgs.AddParam("copyright", string.Empty, "(C)");
                 */
                #endregion

                fileInfo = new FileInfo(dirName + "FixRec.html");

				// writer = new XmlTextWriter(fileInfo.FullName, null);
                writer = new StreamWriter(fileInfo.FullName);

				// Load the XSLT
                /*
				XPathNavigator nav = new XPathDocument(assemblyPath + "fixRecommendation.xsl").CreateNavigator();
				XslCompiledTransform xslt = new XslCompiledTransform();
				xslt.Load(nav);
                 */

				// Create the HTML
				// xslt.Transform(_issue.IssueType.Advisory.CreateNavigator(), xslFixRecommendationArgs, writer);
                writer.Write(_issue.IssueType.Advisory.InnerText);
			}
			finally
			{
				// Close writer
				if (writer != null)
				{
					writer.Flush();
					writer.Close();
				}
			}

			return fileInfo.FullName;
		}

		private string GenerateDescription()
		{
			StringBuilder desc = new StringBuilder();
			try
			{
				XmlDocument advisory = _issue.IssueType.Advisory;
				XPathNavigator nav = advisory.CreateNavigator();

				desc.AppendLine(nav.SelectSingleNode("/advisory/name").Value);
				desc.AppendLine();

				desc.AppendLine(nav.SelectSingleNode("/advisory/testDescription").Value);
				desc.AppendLine();

				desc.Append("WASC Threat Classification: ");
				desc.AppendLine(nav.SelectSingleNode("/advisory/threatClassification/name").Value);
				desc.AppendLine();

				desc.Append("Security Risk: ");
				desc.AppendLine(nav.SelectSingleNode("/advisory/securityRisks/securityRisk").Value);
				desc.AppendLine();

				desc.Append("Possible Causes: ");
				desc.AppendLine(nav.SelectSingleNode("/advisory/causes/cause").Value);
				desc.AppendLine();
			}
			catch (Exception)
			{
			}
			return desc.ToString();
		}

		private void LogDefectForm_Load(object sender, EventArgs e)
		{
			DoOnLoadUpdates();
		}

		private void AddFileToListView(string fileName)
		{
			ListViewItem item;

			FileInfo file = new FileInfo(fileName);

			filesListView.BeginUpdate();

			// Set a default icon for the file.
			Icon iconForFile = SystemIcons.WinLogo;

			item = new ListViewItem(file.Name, 1);

			try
			{
				iconForFile = Icon.ExtractAssociatedIcon(file.FullName);
			}
			catch (Exception)
			{
				// failed to get icon
			}

			item.ToolTipText = file.FullName;
			item.Tag = file.FullName;
			
			
		
			// Check to see if the image collection contains an image
			// for this extension, using the extension as a key.
			if (!iconImageList.Images.ContainsKey(file.Extension))
			{
				iconImageList.Images.Add(file.Extension, iconForFile);
			}
			item.ImageKey = file.Extension;
			filesListView.Items.Add(item);
			filesListView.EndUpdate();
		}

		private void mnuOpen_Click(object sender, EventArgs e)
		{
			ListView.SelectedListViewItemCollection selected = filesListView.SelectedItems;
			foreach (ListViewItem item in selected)
			{
				try
				{
					// get the filename
					string filename = (string)item.Tag;
					ProcessStartInfo psi = new ProcessStartInfo();
					psi.Verb = "Open";
					psi.FileName = filename;
					Process.Start(psi);
				}
				catch (Exception)
				{
					MessageBox.Show("Unable to open the selected file.");
				}
			}
		}

		private void mnuEdit_Click(object sender, EventArgs e)
		{
			ListView.SelectedListViewItemCollection selected = filesListView.SelectedItems;
			foreach (ListViewItem item in selected)
			{
				try
				{
					// get the filename
					string filename = (string)item.Tag;
					ProcessStartInfo psi = new ProcessStartInfo();
					psi.Verb = "Edit";
					psi.FileName = filename;
					Process.Start(psi);
				}
				catch (Exception)
				{
					MessageBox.Show("Unable to edit the selected file.");
				}
			}
		}

		private void mnuRemove_Click(object sender, EventArgs e)
		{
			ListView.SelectedListViewItemCollection selected = filesListView.SelectedItems;
			foreach (ListViewItem item in selected)
			{
				filesListView.Items.Remove(item);
			}
		}

		private void btnAdd_Click(object sender, EventArgs e)
		{
			DialogResult res = openFileDialog1.ShowDialog(this);
			if (res == DialogResult.OK)
			{
				string fileName = openFileDialog1.FileName;
				if (fileName != null && System.IO.File.Exists(fileName))
				{
					AddFileToListView(fileName);
				}
			}
		}

		private void btnOpen_Click(object sender, EventArgs e)
		{
			mnuOpen_Click(sender, e);
		}

		private void btnEdit_Click(object sender, EventArgs e)
		{
			mnuEdit_Click(sender, e);  
		}

		private void btnRemove_Click(object sender, EventArgs e)
		{
			mnuRemove_Click(sender, e);
		}

		private void DeleteTempFiles(object sender, FormClosedEventArgs e)
		{
			try
			{
				DirectoryInfo dir = new DirectoryInfo(_tempDir);
				FileInfo[] files = dir.GetFiles();
				foreach (FileInfo fi in files)
					fi.Delete();
				dir.Delete();
			}
			catch (Exception)
			{
			}
		}
	}
}